/* Emacs style mode select   -*- C++ -*-
 *-----------------------------------------------------------------------------
 *
 *
 *  PrBoom: a Doom port merged with LxDoom and LSDLDoom
 *  based on BOOM, a modified and improved DOOM engine
 *  Copyright (C) 1999 by
 *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
 *  Copyright (C) 1999-2000 by
 *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
 *  Copyright 2005, 2006 by
 *  Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *
 * DESCRIPTION:  tracers stuff
 *
 *-----------------------------------------------------------------------------
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "doomdef.h"
#include "doomstat.h"
#include "m_argv.h"
#include "m_misc.h"
#include "lprintf.h"

#include "hu_tracers.h"

dboolean traces_present;

hu_textline_t w_traces[NUMTRACES];

void TracerApply(tracertype_t index);
void GivenDamageReset(tracertype_t index);
void GivenDamageApply(tracertype_t index);

traceslist_t traces[NUMTRACES];

void InitTracers(void)
{
  int i, p;
  long value, count;

  traceslistinit_t traces_init[NUMTRACES] = {
    {"-trace_thingshealth", "health ", TracerApply, NULL},
    {"-trace_thingspickup", "pickup ", TracerApply, NULL},
    {"-trace_linescross"  , "lcross ", TracerApply, NULL},
    {"-trace_givendamage" , "damage ", GivenDamageApply, GivenDamageReset},
  };

  traces_present = false;

  for (i = 0; i < NUMTRACES; i++)
  {
    strcpy(traces[i].cmd, traces_init[i].cmd);
    strcpy(traces[i].prefix, traces_init[i].prefix);
    traces[i].ApplyFunc = traces_init[i].ApplyFunc;
    traces[i].ResetFunc = traces_init[i].ResetFunc;

    count = 0;
    traces[i].count = 0;
    if ((p = M_CheckParm(traces[i].cmd)) && (p < myargc - 1))
    {
      while (count < 3 && p + count < myargc - 1 && M_StrToInt(myargv[p + 1 + count], &value))
      {
        sprintf(traces[i].items[count].value, "\x1b\x36%ld\x1b\x33 0", value);
        traces[i].items[count].index = value;

        if (traces[i].ApplyFunc)
          traces[i].ApplyFunc(i);

        traces_present = true;
        count++;
      }
      traces[i].count = count;
    }
  }
}

void TracerApply(tracertype_t index)
{
  int i;

  strcpy(traces[index].hudstr, traces[index].prefix);
  for (i = 0; i < traces[index].count; i++)
  {
    sprintf(traces[index].hudstr + strlen(traces[index].hudstr),
      "\x1b\x33%s ", traces[index].items[i].value);
  }
}

void CheckThingsPickupTracer(mobj_t *mobj)
{
  if (traces[TRACE_PICKUP].count)
  {
    int i;
    for (i = 0; i < traces[TRACE_PICKUP].count; i++)
    {
      if (mobj->index == traces[TRACE_PICKUP].items[i].index)
      {
        sprintf(traces[TRACE_PICKUP].items[i].value,
          "\x1b\x36%d \x1b\x33%05.2f",
          traces[TRACE_PICKUP].items[i].index, (float)(leveltime)/35);
      }
    }
  }
}

void CheckThingsHealthTracer(mobj_t *mobj)
{
  if (traces[TRACE_HEALTH].count)
  {
    int i;
    for (i = 0; i < traces[TRACE_HEALTH].count; i++)
    {
      if (mobj->index == traces[TRACE_HEALTH].items[i].index)
      {
        sprintf(traces[TRACE_HEALTH].items[i].value,
          "\x1b\x36%d \x1b\x33%d",
          mobj->index, mobj->health);
      }
    }
  }
}

int crossed_lines_count = 0;
void CheckLinesCrossTracer(line_t *line)
{
  if (traces[TRACE_CROSS].count)
  {
    int i;
    crossed_lines_count++;
    for (i = 0;i < traces[TRACE_CROSS].count; i++)
    {
      if (line->iLineID == traces[TRACE_CROSS].items[i].index)
      {
        if (!traces[TRACE_CROSS].items[i].data1)
        {
          sprintf(traces[TRACE_CROSS].items[i].value,
            "\x1b\x36%d \x1b\x33%05.2f",
            traces[TRACE_CROSS].items[i].index, (float)(leveltime)/35);
          traces[TRACE_CROSS].items[i].data1 = 1;
        }
      }
    }
  }
}

void ClearLinesCrossTracer(void)
{
  if (traces[TRACE_CROSS].count)
  {
    if (!crossed_lines_count)
    {
      int i;
      for (i = 0; i < traces[TRACE_CROSS].count; i++)
      {
        traces[TRACE_CROSS].items[i].data1 = 0;
      }
    }
    crossed_lines_count = 0;
  }
}

static int given_damage_pertic[MAXTRACEITEMS];
static int given_damage_pertic_saved[MAXTRACEITEMS];
static int given_damage_total[MAXTRACEITEMS];
static int given_damage_processed[MAXTRACEITEMS];

void CheckGivenDamageTracer(mobj_t *mobj, int damage)
{
  if (traces[TRACE_DAMAGE].count)
  {
    int i;
    for (i = 0; i < traces[TRACE_DAMAGE].count; i++)
    {
      if (mobj->index == traces[TRACE_DAMAGE].items[i].index)
      {
        given_damage_processed[i] = false;
        given_damage_pertic[i] += damage;
        given_damage_total[i] += damage;
      }
    }
  }
}

void GivenDamageApply(tracertype_t index)
{
  if (traces[index].count)
  {
    int i;
    for (i = 0; i < traces[index].count; i++)
    {
      if (!given_damage_processed[i])
      {
        given_damage_processed[i] = true;
        given_damage_pertic_saved[i] = given_damage_pertic[i];
      }

      sprintf(traces[index].items[i].value,
        "\x1b\x36%d \x1b\x33%d/\x1b\x33%d",
        traces[index].items[i].index, given_damage_pertic_saved[i], given_damage_total[i]);
      TracerApply(index);
    }
  }
}

void GivenDamageReset(tracertype_t index)
{
  int i;
  for (i = 0; i < traces[index].count; i++)
  {
    given_damage_pertic[i] = 0;
  }
}

typedef struct {
  int init_index;
  int index;
} PACKEDATTR tracer_mapthing_t;

static tracer_mapthing_t *deathmatchstarts_indexes = NULL;
static tracer_mapthing_t playerstarts_indexes[MAXPLAYERS];
int num_deathmatchstarts_indexes = 0;

void TracerAddDeathmatchStart(int num, int index)
{
  if (num >= num_deathmatchstarts_indexes)
  {
    num_deathmatchstarts_indexes = num + 1;

    deathmatchstarts_indexes = realloc(
      deathmatchstarts_indexes,
      num_deathmatchstarts_indexes * sizeof(deathmatchstarts_indexes[0]));
  }

  deathmatchstarts_indexes[num].index = index;
}

void TracerAddPlayerStart(int num, int index)
{
  if (traces_present)
  {
    //init
    if (gametic == 0)
    {
      playerstarts_indexes[num].init_index = index;
      playerstarts_indexes[num].index = index;
    }
    else
    {
      playerstarts_indexes[num].index = playerstarts_indexes[num].init_index;
    }
  }
}

int TracerGetDeathmatchStart(int index)
{
  if (index >= num_deathmatchstarts_indexes)
    I_Error("TracerGetDeathmatchStart: index out of bounds");

  return deathmatchstarts_indexes[index].index;
}

int TracerGetPlayerStart(int index)
{
  if (index >= sizeof(playerstarts_indexes) / sizeof(playerstarts_indexes[0]))
    I_Error("TracerGetDeathmatchStart: index out of bounds");

  return playerstarts_indexes[index].index;
}

void TracerClearStarts(void)
{
  int i;

  for (i = 0; i < sizeof(playerstarts_indexes) / sizeof(playerstarts_indexes[0]); i++)
  {
    playerstarts_indexes[i].index = 0;
  }

  num_deathmatchstarts_indexes = 0;
  if (deathmatchstarts_indexes)
  {
    free(deathmatchstarts_indexes);
    deathmatchstarts_indexes = NULL;
  }
}